package main

import (
	"context"
	"flag"
	"fmt"
	"github.com/gin-gonic/gin"
	"go-programming-tour-book/blog_service/global"
	"go-programming-tour-book/blog_service/internal/model"
	"go-programming-tour-book/blog_service/internal/routers"
	"go-programming-tour-book/blog_service/pkg/logger"
	"go-programming-tour-book/blog_service/pkg/setting"
	"go-programming-tour-book/blog_service/pkg/tracer"
	"gopkg.in/natefinch/lumberjack.v2"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	port    string
	runMode string
	config  string
)

var (
	isVersion    bool
	buildTime    string
	buildVersion string
	gitCommitID  string
)

// 控制应用程序的初始化流程
func init() {
	err := setupSetting()
	if err != nil {
		log.Fatalf("init.setupSetting err: %v", err)
	}

	err = setupDBEngine()
	if err != nil {
		log.Fatalf("init.setupDBEngine err: %v", err)
	}

	err = setupLogger()
	if err != nil {
		log.Fatalf("init.setupLogger err: %v", err)
	}

	err = setupTracer()
	if err != nil {
		log.Fatalf("init setupTracer err: %v", err)
	}

	setupFlag()
}

// @title 博客系统
// @version 1.0
// @description Go 语言编程之旅：一起用 Go 做项目
// @termsOfService https://github.com/go-programming-tour-book
func main() {
	if isVersion {
		fmt.Printf("build_time: %s\n", buildTime)
		fmt.Printf("build_version: %s\n", buildVersion)
		fmt.Printf("git_commit_id: %s\n", gitCommitID)
		return
		/*
			执行下述命令，将编译时间、版本号 和 Git Hash（前提是安装了 Git，并且这个应用目录是一个 Git 仓库，否则将无法取到值）设置进去：
			$ go build -ldflags \
			"-X main.buildTime-`data +%Y-%m-%d,%H:%M:%S` -X main.buildVersion=1.0.0 -X main.gitCommitID=`git rev-parse HEAD`"

			查看编译后的二进制文件和版本信息：
			$ ./blog_service -version
			输出结果为：
			build_time: 2023-11-25,23:11:08
			build_version: 1.0.0
			git_commit_id: c572f4218fd9649......
		*/
	}

	gin.SetMode(global.ServerSetting.RunMode)
	router := routers.NewRouter()
	s := &http.Server{
		Addr:           ":" + global.ServerSetting.HttpPort, //监听的 TCP Endpoint
		Handler:        router,                              //路由处理器
		ReadTimeout:    global.ServerSetting.ReadTimeout,    //允许读入的最大时间
		WriteTimeout:   global.ServerSetting.WriteTimeout,   //允许写入的最大时间
		MaxHeaderBytes: 1 << 20,                             //请求头的最大字节数
	}
	//s.ListenAndServe() //监听
	//优雅重启与停止的修改
	go func() { //一直阻塞的监听并处理传入的 HTTP 请求
		err := s.ListenAndServe() //监听
		if err != nil && err != http.ErrServerClosed {
			log.Fatalf("s.ListenAndServe err: %v", err)
		}
	}()

	//等待中断信号
	quit := make(chan os.Signal)
	//设置要捕获的信号: syscall.SIGINT 和 syscall.SIGTERM 信号
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit //捕获成功，进入关机流程
	//打印日志，提醒服务器即将关闭
	log.Println("Shutting down server...")

	//最大时间控制，用于通知该服务端它有 5 秒时间来处理原有的请求
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	//通过 Shutdown 优雅的关闭服务器
	if err := s.Shutdown(ctx); err != nil {
		log.Fatal("Server forced to shutdown:", err)
	}
	log.Println("Server exiting")
}

// 把配置文件内容映射到应用配置结构体中
func setupSetting() error {
	s, err := setting.NewSetting(strings.Split(config, ",")...)
	if err != nil {
		return err
	}
	err = s.ReadSection("Server", &global.ServerSetting)
	if err != nil {
		return err
	}
	err = s.ReadSection("App", &global.AppSetting)
	if err != nil {
		return err
	}
	err = s.ReadSection("Database", &global.DatabaseSetting)
	if err != nil {
		return err
	}
	err = s.ReadSection("JWT", &global.JWTSetting)
	if err != nil {
		return err
	}
	err = s.ReadSection("email", &global.EmailSetting)
	if err != nil {
		return err
	}

	global.ServerSetting.ReadTimeout *= time.Second
	global.ServerSetting.WriteTimeout *= time.Second
	global.JWTSetting.Expire *= time.Second
	global.AppSetting.DefaultContextTimeout *= time.Second

	if port != "" {
		global.ServerSetting.HttpPort = port
	}
	if runMode != "" {
		global.ServerSetting.RunMode = runMode
	}

	return nil
}

func setupDBEngine() error {
	var err error
	global.DBEngine, err = model.NewDBEngine(global.DatabaseSetting)
	if err != nil {
		return err
	}
	return nil
}

func setupLogger() error {
	fileName := global.AppSetting.LogSavePath + "/" + global.AppSetting.LogFileName + global.AppSetting.LogFileExt
	global.Logger = logger.NewLogger(&lumberjack.Logger{
		Filename:  fileName,
		MaxSize:   600,  //日志文件所允许的最大占用空间
		MaxAge:    10,   //日志文件的最大生存周期
		LocalTime: true, //日志文件名的时间格式为本地时间
	}, "", log.LstdFlags).WithCaller(2)

	return nil
}

func setupTracer() error {
	jaegerTracer, _, err := tracer.NewJaegerTracer(
		"blog_service",
		"192.168.239.128:6831",
	)
	if err != nil {
		return err
	}
	global.Tracer = jaegerTracer
	return nil
}

func setupFlag() error {
	flag.StringVar(&port, "port", "", "启动端口")
	flag.StringVar(&runMode, "mode", "", "启动模式")
	flag.StringVar(&config, "config", "configs/", "指定要使用的配置文件路径")

	flag.BoolVar(&isVersion, "version", false, "编译信息")
	flag.Parse()

	return nil
}
